/*
 * This file is part of Grocy Android.
 *
 * Grocy Android is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Grocy Android is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Grocy Android. If not, see http://www.gnu.org/licenses/.
 *
 * Copyright (c) 2020-2023 by Patrick Zedler and Dominic Zedler
 */
package com.gitee.wsl.common.zxing

import android.Manifest
import android.annotation.TargetApi
import android.app.Activity
import android.app.AlertDialog
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Handler
import android.util.Log
import android.view.WindowManager
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.gitee.wsl.common.more.R
import com.gitee.wsl.android.ext.device.globalVibratorManager
import com.gitee.wsl.android.ext.device.tick
import com.google.zxing.client.android.InactivityTimer
import com.google.zxing.client.android.Intents
import com.journeyapps.barcodescanner.BarcodeCallback
import com.journeyapps.barcodescanner.BarcodeResult
import com.journeyapps.barcodescanner.CameraPreview
import com.journeyapps.barcodescanner.DecoratedBarcodeView

class ZXingScanCaptureManager(
    private val activity: Activity,
    private val barcodeView: DecoratedBarcodeView,
    private val barcodeListener: BarcodeListener
) {
    private var showDialogIfMissingCameraPermission = true
    private var missingCameraPermissionDialogMessage = ""
    private var destroyed = false
    private val inactivityTimer: InactivityTimer= InactivityTimer(activity) {
        Log.d(TAG, "Finishing due to inactivity")
        finish()
    }
    private val handler: Handler
    private var finishWhenClosed = false

    val callback = BarcodeCallback { result ->
        globalVibratorManager.tick()
        barcodeView.pause()
        inactivityTimer.cancel()
        barcodeListener.onBarcodeResult(result)
    }

    fun interface BarcodeListener {
        fun onBarcodeResult(result: BarcodeResult)
    }

    /**
     * Start decoding.
     */
    fun decode() {
        barcodeView.decodeSingle(callback)
    }

    /**
     * Call from Activity#onResume().
     */
    fun onResume() {
        if (Build.VERSION.SDK_INT >= 23) {
            openCameraWithPermission()
        } else {
            barcodeView.resume()
        }
        activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        inactivityTimer.start()
    }

    private var askedPermission = false

    init {
        barcodeView.barcodeView.addStateListener(
            object : CameraPreview.StateListener {
                override fun previewSized() {}
                override fun previewStarted() {}
                override fun previewStopped() {}
                override fun cameraError(error: Exception) {
                    displayFrameworkBugMessageAndExit(
                        activity.getString(R.string.zxing_msg_camera_framework_bug)
                    )
                }

                override fun cameraClosed() {
                    if (finishWhenClosed) {
                        Log.d(TAG, "Camera closed; finishing activity")
                        finish()
                    }
                }
            }
        )
        handler = Handler()
    }

    @TargetApi(23)
    private fun openCameraWithPermission() {
        if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
            == PackageManager.PERMISSION_GRANTED
        ) {
            barcodeView.resume()
        } else if (!askedPermission) {
            ActivityCompat.requestPermissions(
                activity, arrayOf(Manifest.permission.CAMERA),
                cameraPermissionReqCode
            )
            askedPermission = true
        } // else wait for permission result
    }

    /**
     * Call from Activity#onRequestPermissionsResult
     *
     * @param requestCode  The request code passed in [ActivityCompat.requestPermissions].
     * @param permissions  The requested permissions.
     * @param grantResults The grant results for the corresponding permissions which is either [                     ][PackageManager.PERMISSION_GRANTED] or [PackageManager.PERMISSION_DENIED].
     * Never null.
     */
    fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String?>?,
        grantResults: IntArray
    ) {
        if (requestCode == cameraPermissionReqCode) {
            if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted
                barcodeView.resume()
            } else {
                setMissingCameraPermissionResult()
                if (showDialogIfMissingCameraPermission) {
                    displayFrameworkBugMessageAndExit(missingCameraPermissionDialogMessage)
                } else {
                    closeAndFinish()
                }
            }
        }
    }

    /**
     * Call from Activity#onPause().
     */
    fun onPause() {
        activity.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        inactivityTimer.cancel()
        barcodeView.pause()
    }

    /**
     * Call from Activity#onDestroy().
     */
    fun onDestroy() {
        destroyed = true
        inactivityTimer.cancel()
        handler.removeCallbacksAndMessages(null)
    }

    private fun finish() {
        activity.finish()
    }

    protected fun closeAndFinish() {
        if (!barcodeView.barcodeView.isCameraClosed) {
            finishWhenClosed = true
        }
        barcodeView.pause()
        inactivityTimer.cancel()
    }

    private fun setMissingCameraPermissionResult() {
        val intent = Intent(Intents.Scan.ACTION)
        intent.putExtra(Intents.Scan.MISSING_CAMERA_PERMISSION, true)
        activity.setResult(Activity.RESULT_CANCELED, intent)
    }

    protected fun displayFrameworkBugMessageAndExit(message: String) {
        var message = message
        if (activity.isFinishing || destroyed || finishWhenClosed) {
            return
        }
        if (message.isEmpty()) {
            message = activity.getString(R.string.zxing_msg_camera_framework_bug)
        }
        val builder = AlertDialog.Builder(activity)
        builder.setTitle(activity.getString(R.string.zxing_app_name))
        builder.setMessage(message)
        builder.setPositiveButton(R.string.zxing_button_ok) { dialog, which -> finish() }
        builder.setOnCancelListener { dialog: DialogInterface? -> finish() }
        builder.show()
    }

    /**
     * If set to true, shows the default error dialog if camera permission is missing.
     *
     *
     * If set to false, instead the capture manager just finishes.
     *
     *
     * In both cases, the activity result is set to [Intents.Scan.MISSING_CAMERA_PERMISSION] and
     * cancelled
     */
    fun setShowMissingCameraPermissionDialog(visible: Boolean) {
        setShowMissingCameraPermissionDialog(visible, "")
    }

    /**
     * If set to true, shows the specified error dialog message if camera permission is missing.
     *
     *
     * If set to false, instead the capture manager just finishes.
     *
     *
     * In both cases, the activity result is set to [Intents.Scan.MISSING_CAMERA_PERMISSION] and
     * cancelled
     */
    fun setShowMissingCameraPermissionDialog(visible: Boolean, message: String?) {
        showDialogIfMissingCameraPermission = visible
        missingCameraPermissionDialogMessage = message ?: ""
    }

    companion object {
        private val TAG = ZXingScanCaptureManager::class.java.simpleName
        var cameraPermissionReqCode = 250
    }
}
